package com.vineetmanohar.nitrogen;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.XML;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;

public class ModelFactory {
	public static ObjectModel parseModelFile(File modelFile) {
		ModelSourceType modelSourceType = ModelSourceType
				.createFromFile(modelFile);
		if (modelSourceType == null) {
			throw new RuntimeException("File type not supported: "
					+ modelFile.getAbsolutePath());
		}

		String modelText;
		try {
			modelText = FileUtils.readFileToString(modelFile);
		} catch (IOException e) {
			throw new NitrogenException("Unable to read file ", e);
		}
		switch (modelSourceType) {
		case JSON:
			return parseJson(modelText);
		case XML:
			return parseXml(modelText);
		default:
			throw new RuntimeException("Model type not supported: "
					+ modelFile.getAbsolutePath());
		}
	}

	private static ObjectModel parseJson(String modelText) {
		JsonElement jsonElement = new JsonParser().parse(modelText);

		return parseJsonElement(jsonElement).getObjectModel();
	}

	private static ObjectModel parseXml(String modelText) {
		try {
			JSONObject jsonObject = XML.toJSONObject(modelText);
			String json = jsonObject.toString();
			return parseJson(json);
		} catch (JSONException e) {
			throw new RuntimeException("Invalid XML", e);
		}
	}

	private static ObjectModel parseJsonObject(JsonObject jsonObject) {
		ObjectModel objectModel = createObjectModel();

		for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
			objectModel.addChild(entry.getKey(),
					parseJsonElement(entry.getValue()));
		}

		return objectModel;
	}

	private static WritableModel parseJsonElement(JsonElement jsonElement) {
		if (jsonElement.isJsonObject()) {
			ObjectModel objectModel = parseJsonObject(jsonElement
					.getAsJsonObject());
			return objectModel;
		}
		if (jsonElement.isJsonPrimitive()) {
			PrimitiveModel primitiveModel = parseJsonPrimitive(jsonElement
					.getAsJsonPrimitive());
			return primitiveModel;
		}
		if (jsonElement.isJsonArray()) {
			ArrayModel arrayModel = parseJsonArray(jsonElement.getAsJsonArray());
			return arrayModel;
		}
		if (jsonElement.isJsonNull()) {
			return null;
		}

		return null;
	}

	private static PrimitiveModel parseJsonPrimitive(JsonPrimitive jsonPrimitive) {
		return createPrimitive(jsonPrimitive.getAsString());
	}

	private static ArrayModel parseJsonArray(JsonArray jsonArray) {
		ArrayModel arrayModel = createArray();

		for (JsonElement jsonElement : jsonArray) {
			WritableModel writableModel = parseJsonElement(jsonElement);
			arrayModel.addChild(writableModel);
		}

		return arrayModel;
	}

	private static ArrayModel createArray() {
		return new ArrayModel();
	}

	private static ObjectModel createObjectModel() {
		return new ObjectModel();
	}

	public static PrimitiveModel createPrimitive(String value) {
		return new PrimitiveModel(value);
	}
}